package com.woniuxy.project115.kaer.user.interceptor;

import com.woniuxy.project115.kaer.commons.util.JwtUtil;
import com.woniuxy.project115.kaer.user.util.RedisKey;
import io.jsonwebtoken.Claims;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.security.auth.message.AuthException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

@Component
public class Loginlnterceptor implements HandlerInterceptor {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if(request.getMethod().equalsIgnoreCase("OPTIONS")) {
            return true;
        }
        //取出请求头
        String accessToken = request.getHeader("token");
        if (accessToken == null) {
            throw new AuthException("令牌不存在");
        }
        //判断短token是否到期
        if (stringRedisTemplate.hasKey(RedisKey.ACCESS_EXPIRE(accessToken))) {
            return true;
        }

        synchronized (accessToken.intern()){
            //加锁保证同一个短token在续期时排队执行，不是同一个则继续执行续期
            if (stringRedisTemplate.hasKey(RedisKey.ACCESS_EXPIRE(accessToken))) {
                return true;
            }
            //判断长token是否过期
            if (stringRedisTemplate.getExpire(RedisKey.ACCESS_REFRESH(accessToken), TimeUnit.SECONDS)/60>=5){
                final String refreshToken = stringRedisTemplate.opsForValue().get(RedisKey.ACCESS_REFRESH(accessToken));
                final Claims claims = JwtUtil.parseToken(refreshToken);
                //获取长token的过期时间，判断是否大于五分钟，避免零值的问题
                final String newAccessToken = JwtUtil.createToken(claims, 30);
                final String newRefreshToken = JwtUtil.createToken(claims, 3 * 24 * 60);
                //通过响应头将jwt响应给客户端
                response.setHeader("token", newAccessToken);
                //在跨域访问时必须公开请求头，前端才能获取列
                response.setHeader("Access-Control-Expose-Headers", "token");
                //保存新的长token和短token
                //保存短token的过期时间
                stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_EXPIRE(newAccessToken),"",30, TimeUnit.MINUTES );
                //保存长token
                stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_REFRESH(newAccessToken),newRefreshToken,3 * 24 * 60,TimeUnit.MINUTES );
                //为之前的短token延时避免发生并发时重复续期
                stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_EXPIRE(accessToken),"",1L, TimeUnit.MINUTES );
                //延时删除之前的长token
                rabbitTemplate.convertAndSend("delete_token_queue",RedisKey.ACCESS_REFRESH(accessToken));
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(60000);
                            stringRedisTemplate.delete(RedisKey.ACCESS_REFRESH(accessToken));
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }).start();
            }else {
                throw new AuthException("令牌无效");
            }
        }
        return true;
    }
}
